//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using OpenLS.Spreadsheet.StandardAddIn.Statistics;

namespace OpenLS.Spreadsheet.StandardAddIn
{
    public static class LoanFunctions
    {
        public static double PresentValueInterestFactor(double rate, double periodCount)
        {
            return MathHelper.XPlus1PowerY(rate, periodCount);
        }

        public static double FutureValueInterestFactorsAnnuity(double rate, double periodCount)
        {
            if (rate == 0)
                return periodCount;
            return MathHelper.XPlus1PowerYMinus1(rate, periodCount)/rate;
        }


        public static double Payment(double rate, double periodCount, double presentValue, double futureValue,
                                     PaymentType type)
        {
            return
                ((-presentValue*PresentValueInterestFactor(rate, periodCount) - futureValue)/
                 ((1.0 + rate*(int) type)*FutureValueInterestFactorsAnnuity(rate, periodCount)));
        }

        public static double InterestPart(double rate, double periodCount, double presentValue, double payment,
                                          PaymentType type)
        {
            return -(presentValue*MathHelper.XPlus1PowerY(rate, periodCount)*rate +
                     payment*MathHelper.XPlus1PowerYMinus1(rate, periodCount));
        }

        public static double InterestPayment(double rate, double periodCount, double periodCount2, double presentValue,
                                             double futureValue, PaymentType type)
        {
            double payment = Payment(rate, periodCount2, presentValue, futureValue, type);
            return InterestPart(rate, periodCount - 1, presentValue, payment, type);
        }


        internal static double FutureValue(double rate, double periodCount, double payment, double presentValue,
                                           PaymentType type)
        {
            double pvif = PresentValueInterestFactor(rate, periodCount);
            double fvifa = FutureValueInterestFactorsAnnuity(rate, periodCount);
            return -((presentValue*pvif) + payment*(1.0 + rate*(int) type)*fvifa);
        }

        internal static double PresentValue(double rate, double nper, double pmt, double fv, PaymentType type)
        {
            double pvif = PresentValueInterestFactor(rate, nper);
            double fvifa = FutureValueInterestFactorsAnnuity(rate, nper);
            if (pvif == 0)
                throw new DivideByZeroException();
            return (-fv - pmt*(1.0 + rate*(int) type)*fvifa)/pvif;
        }

        internal static double Rate(double periodCount, double payment, double presentValue, double futureValue,
                                    PaymentType type, double guess)
        {
            BrentSolver s = new BrentSolver(
                x => FutureValue(x, periodCount, payment, presentValue, type) - futureValue);
            var result=  s.Solve(0d, 1); // no need for guess
            Debug.WriteLine("rate = " + result + "future value = " + FutureValue(result, periodCount, payment, presentValue, type) + " should be " + futureValue);
            return result;
        }

        public static object PrincipalPayment(double rate, double per, double nper, double pv, double fv, PaymentType type)
        {
            var pmt = LoanFunctions.Payment(rate, nper, pv, fv, type)
                ;
            var ipmt = LoanFunctions.InterestPayment(rate, per, nper, pv, fv, type);
            return pmt - ipmt
                ;
        }
    }
}